/*
** Copyright (c) 2010  Kimmo 'Rainy' Pekkola
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program.  If not, see http://www.gnu.org/licenses.
*/

#include "usagegrid.h"
#include "scanwindow.h"

//-----------------------------------------------------------------------------
/**
** Constructor
**
** \param pParent The parent for the widget.
*/
CUsageGrid::CUsageGrid(QWidget* pParent, QTreeView* pTreeView, int limit) : QWidget(pParent)
{
    m_pTreeView = pTreeView;
    m_pRootItem = NULL;
    m_pRecursive = false;
    m_Limit = limit;

    clear();
}

//-----------------------------------------------------------------------------
/**
** Destructor
*/
CUsageGrid::~CUsageGrid()
{
}

//-----------------------------------------------------------------------------
/**
** Clears the grid image.
*/
void CUsageGrid::clear()
{
    m_pxGrid = QPixmap(size());
    m_pxGrid.fill(palette().background().color());
    m_pRootItem = NULL;
    setSelectionRect(QRect());  // Clear the selection since it won't be accurate anymore
}

//-----------------------------------------------------------------------------
/**
** Initializes the grid.
**
** \param pRootItem The root item for the grid.
** \param bRecursive If true the items are scanned recursively.
*/
void CUsageGrid::initialize(QStandardItem* pRootItem, bool bRecursive)
{
    m_pRootItem = pRootItem;
    m_pRecursive = bRecursive;

    if (m_pRootItem)
    {
        createGrid(m_pRootItem, bRecursive);
    }
}

//-----------------------------------------------------------------------------
/**
** Selects the item on the given position.
**
** \param pItem The root item
** \param pos The mouse position on the grid.
** \return The selected item.
*/
QStandardItem* CUsageGrid::pickItem(QStandardItem* pItem, QPoint pos)
{
    QRect r = pItem->data(AreaRole).toRect();
    if (r.contains(pos))
    {
        if (pItem->rowCount() > 0 && (m_pRecursive || pItem == m_pRootItem))
        {
            for (int i = 0; i < pItem->rowCount(); i++)
            {
                QStandardItem* pChild = pickItem(pItem->child(i), pos);
                if (pChild)
                {
                    return pChild;
                }
            }
        }
        return pItem;
    }
    return NULL;
}

//-----------------------------------------------------------------------------
/**
** Creates a color for the item. All similar items should have a similar color.
**
** \param strText The text of the item.
** \param bExtensionOnly If true only the extension determines the color.
** \return The color for the item.
*/
QColor CUsageGrid::getBaseColor(QString strText, bool bExtensionOnly)
{
    quint32 hash = 0x808080;
    if (bExtensionOnly)
    {
        int pos = strText.lastIndexOf('.');
        if (pos != -1)
        {
            hash = qHash(strText.mid(pos));
        }
    }
    else
    {
        hash = qHash(strText);
    }

    QColor color(255, 0, 0);
    color.setHsv(hash + rand() % 10, 200, 255, 255);
    return color;
}

//-----------------------------------------------------------------------------
/**
** Creates the grid for the given item.
**
** \param pItem The root item.
** \param bRecursive If true the items are scanned recursively.
*/
void CUsageGrid::createGrid(QStandardItem* pItem, bool bRecursive)
{
    QTime timer;
    timer.start();

    m_pxGrid = QPixmap(size());
    m_pxGrid.fill(palette().background().color());
    m_pRootItem = pItem;
    setSelectionRect(QRect());  // Clear the selection since it won't be accurate anymore

    QPainter painter(&m_pxGrid);
    QFont font;
    font.setPixelSize(9);
    painter.setFont(font);
    drawGrid(painter, pItem, rect(), bRecursive, 0);

    // qDebug() << "Create grid time: " << timer.elapsed();
}

//-----------------------------------------------------------------------------
/**
** Draws the grid to the image.
**
** \param painter The painter
** \param pItem The currently drawn item.
** \param rect The item's rectangle
** \param bRecursive If true the items are scanned recursively.
** \param level The current recurse level
*/
void CUsageGrid::drawGrid(QPainter& painter, QStandardItem* pItem, const QRectF& rect, bool bRecursive, int level)
{
    if (pItem)
    {
        pItem->setData(rect, AreaRole);

        quint64 total = pItem->data(SizeRole).toULongLong();
        if (total > 0)
        {
            // Calculate the total size for each row so we know their relative sizes
            int rows = (int)sqrt(pItem->rowCount());
            QList<quint64> rowTotals;
            for (int i = 0; i < pItem->rowCount(); i++)
            {
                if (pItem->child(i))
                {
                    quint64 size = pItem->child(i)->data(SizeRole).toULongLong();
                    if (i % rows == 0)
                    {
                        rowTotals.append(size);
                    }
                    else
                    {
                        rowTotals.last() += size;
                    }
                }
            }

            quint64 newRowTotal = 0;
            quint64 newColumnTotal = 0;

            int row = -1;
            qreal x = rect.left();
            qreal y = rect.top();
            qreal h = rect.height();
            qreal w = rect.width();

            if (pItem->rowCount() == 0 || (rect.width() < 2 && rect.height() < 2) || (!bRecursive && pItem->parent()) || level > m_Limit)
            {
                QString strName = pItem->data(Qt::DisplayRole).toString();

                QLinearGradient gradient(rect.topLeft(), rect.bottomRight());

                QColor color = getBaseColor(strName, bRecursive);
#ifdef Q_WS_MAEMO_5
                painter.fillRect(rect, QBrush(color));
#else
                gradient.setColorAt(0, color.lighter());
                gradient.setColorAt(1, color.darker());
                gradient.setSpread(QGradient::PadSpread);
                painter.fillRect(rect, gradient);
#endif
                if (rect.width() > 10 && rect.height() > 5)
                {
                    painter.setPen(Qt::black);
                    painter.drawText(rect, Qt::AlignCenter, strName);
                }
            }
            else
            {
                for (int i = 0; i < pItem->rowCount(); i++)
                {
                    quint64 size = pItem->child(i)->data(SizeRole).toULongLong();

                    if (i % rows == 0)
                    {
                        row++;
                        x = rect.left();
                        y = rect.top() + (rect.height() * newRowTotal / total);
                        h = rect.height() * rowTotals[row] / total;

                        newRowTotal += rowTotals[row];
                        newColumnTotal = 0;
                    }
                    quint64 rowTotal = rowTotals[row] == 0 ? 1 : rowTotals[row];
                    w = rect.width() * size / rowTotal;
                    x = rect.left() + rect.width() * newColumnTotal / rowTotal;

                    newColumnTotal += size;

                    drawGrid(painter, pItem->child(i), QRectF(x, y, w, h), bRecursive, level + 1);
                }
            }
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
/// OVERRIDES
///////////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
/**
** Paint handler. Draws the usage grid to the window. The selection area
** is drawn over the grid.
**
** \param pEvent The paint event.
*/
void CUsageGrid::paintEvent(QPaintEvent* /*pEvent*/)
{
    QPainter painter(this);
    painter.drawPixmap(0, 0, m_pxGrid);

    if (!m_rectSelection.isEmpty() || m_rectSelection.x() != 0 || m_rectSelection.y() != 0)
    {
        QBrush b(QColor(0, 0, 0, 128));

        QRect r = QRect(0, 0, m_rectSelection.left(), m_pxGrid.height());
        painter.fillRect(r, b);
        r = QRect(m_rectSelection.left(), 0, m_rectSelection.width() - 1, m_rectSelection.top());
        painter.fillRect(r, b);
        r = QRect(m_rectSelection.left(), m_rectSelection.bottom(), m_rectSelection.width() - 1, m_pxGrid.height() - m_rectSelection.bottom());
        painter.fillRect(r, b);
        r = QRect(m_rectSelection.right(), 0, m_pxGrid.width() - m_rectSelection.right(), m_pxGrid.height());
        painter.fillRect(r, b);
    }
}

//-----------------------------------------------------------------------------
/**
** Mouse press handler. Locates the clicked item from the tree.
**
** \param pEvent The paint event.
*/
void CUsageGrid::mousePressEvent(QMouseEvent* pEvent)
{
    if (m_pTreeView && m_pRootItem)
    {
        QStandardItem* pItem = pickItem(m_pRootItem, pEvent->pos());
        if (pItem)
        {
            QSortFilterProxyModel* pModel = dynamic_cast<QSortFilterProxyModel*>(m_pTreeView->model());

            if (pModel)
            {
                QModelIndex index = pModel->mapFromSource(pItem->index());
                m_pTreeView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect);
                m_pTreeView->scrollTo(index);
            }
        }
    }
}

// EOF
